/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering,
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 *
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.service.replay.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import cgl.narada.matching.Profile;
import cgl.narada.matching.ProfileReconstructor;
import cgl.narada.service.replay.ReplayRequest;
import cgl.narada.service.replay.ReplayServiceExchange;

/*
 Encapsulates a request that initiates playbacks. A replay request could be
 <br>(a) Based on specified set of sequence numbers
 <br>(b) Based on a specified range of sequence numbers
 <br>(c) Based on (b) in addition to more constraints specified on
 the constraints that the playback events should satisfy.
 @author Hasan Bulut and Shrideep Pallickara
 $Date$
 $Revision$
 */

public class ReplayRequestEvent implements ReplayRequest {
  private int       templateId;
  private int       entityId;

  private boolean   hasRequestIdentifier                  = false;
  private String    replayRequestIdentifier               = null;

  private boolean   hasReplayTopic                        = false;                 // added for rtsp
  private String    replayTopic                           = null;                  // added for rtsp

  private boolean   hasListOfSequencesToReplay            = false;
  private long[]    listOfSequencesToReplay               = null;

  private boolean   hasSpecifiedSequenceRange             = false;
  private long      sequenceStart                         = 0;
  private long      sequenceEnd                           = 0;

  private boolean   hasSpecifiedConstraints               = false;
  private Profile[] constraints                           = null;

  private String    moduleName                            = "ReplayRequestEvent: ";

  private boolean   replayWithTimestampRange              = false;                 // added for rtsp
  private boolean   hasTimestampRange                     = false;                 // added for rtsp
  private long      startTimestamp                        = 0;                     // added for rtsp
  private long      endTimestamp                          = 0;                     // added for rtsp

  private boolean   hasStartingSequenceAndEndingTimestamp = false;                 // added for rtsp
  private long      startingSequence                      = 0;                     // added for rtsp
  private long      endingTimestamp                       = 0;                     // added for rtsp

  private boolean   requestForTimestamp                   = false;                 // added for rtsp
  private long      sequenceNumber                        = 0;                     // added for rtsp

  private boolean   requestSequencesAndTimestamps         = false;                 // added for rtsp

  public ReplayRequestEvent(byte[] marshalledBytes) {
    ByteArrayInputStream baInputStream = new ByteArrayInputStream(
        marshalledBytes);
    DataInputStream din = new DataInputStream(baInputStream);

    try {
      din.readByte();
      /** ReplayServiceExchange.REPLAY_SERVICE_REQUEST */

      templateId = din.readInt();
      entityId = din.readInt();

      /** Deal with request identifier */
      hasRequestIdentifier = din.readBoolean();
      if (hasRequestIdentifier) {
        byte[] idBytes = new byte[din.readInt()];
        din.readFully(idBytes);
        replayRequestIdentifier = new String(idBytes);
      }

      hasReplayTopic = din.readBoolean();
      if (hasReplayTopic) {
        byte[] tpcBytes = new byte[din.readInt()];
        din.readFully(tpcBytes);
        replayTopic = new String(tpcBytes);
      }

      /** Deal with specific sequences */
      hasListOfSequencesToReplay = din.readBoolean();
      if (hasListOfSequencesToReplay) {
        int numOfSequences = din.readInt();
        listOfSequencesToReplay = new long[numOfSequences];
        for (int i = 0; i < numOfSequences; i++) {
          listOfSequencesToReplay[i] = din.readLong();
        }
      }

      hasSpecifiedSequenceRange = din.readBoolean();
      if (hasSpecifiedSequenceRange) {
        sequenceStart = din.readLong();
        sequenceEnd = din.readLong();
      }

      /** Deal with specified constraints */
      hasSpecifiedConstraints = din.readBoolean();
      if (hasSpecifiedConstraints) {
        int numOfConstraints = din.readInt();
        constraints = new Profile[numOfConstraints];

        for (int i = 0; i < numOfConstraints; i++) {
          int _profileType = din.readInt();
          byte[] _profileBytes = new byte[din.readInt()];
          din.readFully(_profileBytes);
          Profile _profile = ProfileReconstructor.getProfile(_profileType,
              _profileBytes);
          constraints[i] = _profile;
        }
      }

      replayWithTimestampRange = din.readBoolean();
      hasTimestampRange = din.readBoolean();
      if (hasTimestampRange || replayWithTimestampRange) {
        startTimestamp = din.readLong();
        endTimestamp = din.readLong();
      }

      hasStartingSequenceAndEndingTimestamp = din.readBoolean();
      if (hasStartingSequenceAndEndingTimestamp) {
        startingSequence = din.readLong();
        endingTimestamp = din.readLong();
      }

      requestForTimestamp = din.readBoolean();
      if (requestForTimestamp) {
        sequenceNumber = din.readLong();
      }

      requestSequencesAndTimestamps = din.readBoolean();

    } catch (IOException ioe) {
      System.out.println(moduleName + "Error while un-marshalling");
    }
    /** end try-catch block*/

  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, long[] listOfSequencesToReplay) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }

    this.listOfSequencesToReplay = listOfSequencesToReplay;
    if (listOfSequencesToReplay != null) {
      hasListOfSequencesToReplay = true;
    }
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, long sequenceStart, long sequenceEnd) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }

    hasSpecifiedSequenceRange = true;
    this.sequenceStart = sequenceStart;
    this.sequenceEnd = sequenceEnd;
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, long sequenceStart, long sequenceEnd,
      Profile[] constraints) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }

    hasSpecifiedSequenceRange = true;
    this.sequenceStart = sequenceStart;
    this.sequenceEnd = sequenceEnd;

    this.constraints = constraints;
    if (constraints != null) {
      hasSpecifiedConstraints = true;
    }
  }

  public byte[] getBytes() {
    ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(baOutputStream);
    byte[] marshalledBytes = null;
    try {
      dout.writeByte(ReplayServiceExchange.REPLAY_SERVICE_REQUEST);

      dout.writeInt(templateId);
      dout.writeInt(entityId);

      /** Deal with request identifier */
      dout.writeBoolean(hasRequestIdentifier);
      if (hasRequestIdentifier) {
        byte[] idBytes = replayRequestIdentifier.getBytes();
        dout.writeInt(idBytes.length);
        dout.write(idBytes);
      }

      dout.writeBoolean(hasReplayTopic);
      if (replayTopic != null) {
        byte[] tpcBytes = replayTopic.getBytes();
        dout.writeInt(tpcBytes.length);
        dout.write(tpcBytes);
      }

      /** Deal with specific sequences */
      dout.writeBoolean(hasListOfSequencesToReplay);
      if (hasListOfSequencesToReplay) {
        int numOfSequencesToReplay = listOfSequencesToReplay.length;
        dout.writeInt(numOfSequencesToReplay);
        for (int i = 0; i < numOfSequencesToReplay; i++) {
          dout.writeLong(listOfSequencesToReplay[i]);
        }
      }

      dout.writeBoolean(hasSpecifiedSequenceRange);
      if (hasSpecifiedSequenceRange) {
        dout.writeLong(sequenceStart);
        dout.writeLong(sequenceEnd);
      }

      /** Deal with specified constraints */
      dout.writeBoolean(hasSpecifiedConstraints);
      if (hasSpecifiedConstraints) {
        int numOfConstraints = constraints.length;
        dout.writeInt(numOfConstraints);
        for (int i = 0; i < numOfConstraints; i++) {
          Profile _profile = constraints[i];
          byte[] _profileBytes = _profile.getBytes();
          dout.writeInt(_profile.getProfileType());
          dout.writeInt(_profileBytes.length);
          dout.write(_profileBytes);
        }
      }

      dout.writeBoolean(replayWithTimestampRange);
      dout.writeBoolean(hasTimestampRange);
      if (hasTimestampRange || replayWithTimestampRange) {
        dout.writeLong(startTimestamp);
        dout.writeLong(endTimestamp);
      }

      dout.writeBoolean(hasStartingSequenceAndEndingTimestamp);
      if (hasStartingSequenceAndEndingTimestamp) {
        dout.writeLong(startingSequence);
        dout.writeLong(endingTimestamp);
      }

      dout.writeBoolean(requestForTimestamp);
      if (requestForTimestamp) {
        dout.writeLong(sequenceNumber);
      }

      dout.writeBoolean(requestSequencesAndTimestamps);

      marshalledBytes = baOutputStream.toByteArray();
    } catch (IOException ioe) {
      System.out.println(moduleName + "Error while marshalling");
    }
    /** end try-catch block*/

    return marshalledBytes;
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, boolean requestForTimestamp,
      long sequenceNumber) {
    this.templateId = templateId;
    this.entityId = entityId;
    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.requestForTimestamp = requestForTimestamp;
    if (requestForTimestamp) {
      this.sequenceNumber = sequenceNumber;
    }
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, String replayTopic,
      boolean hasStartingSequenceAndEndingTimestamp, long startingSequence,
      long endingTimestamp) {
    this.templateId = templateId;
    this.entityId = entityId;
    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.replayTopic = replayTopic;
    if (replayTopic != null) {
      hasReplayTopic = true;
    }
    this.hasStartingSequenceAndEndingTimestamp = hasStartingSequenceAndEndingTimestamp;
    this.startingSequence = startingSequence;
    this.endingTimestamp = endingTimestamp;
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, String replayTopic, long startTimestamp,
      long endTimestamp, boolean replayWithTimestampRange) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.replayTopic = replayTopic;
    if (replayTopic != null) {
      hasReplayTopic = true;
    }
    this.replayWithTimestampRange = replayWithTimestampRange;
    if (replayWithTimestampRange == true) {
      this.startTimestamp = startTimestamp;
      this.endTimestamp = endTimestamp;
    }
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, long startTimestamp, long endTimestamp,
      boolean hasTimestampRange) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.hasTimestampRange = hasTimestampRange;
    if (hasTimestampRange) {
      this.startTimestamp = startTimestamp;
      this.endTimestamp = endTimestamp;
    }
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, boolean requestSequencesAndTimestamps) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.requestSequencesAndTimestamps = requestSequencesAndTimestamps;
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, String replayTopic,
      long[] listOfSequencesToReplay) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.replayTopic = replayTopic;
    if (replayTopic != null) {
      hasReplayTopic = true;
    }
    this.listOfSequencesToReplay = listOfSequencesToReplay;
    if (listOfSequencesToReplay != null) {
      hasListOfSequencesToReplay = true;
    }
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, String replayTopic, long sequenceStart,
      long sequenceEnd) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.replayTopic = replayTopic;
    if (replayTopic != null) {
      hasReplayTopic = true;
    }
    hasSpecifiedSequenceRange = true;
    this.sequenceStart = sequenceStart;
    this.sequenceEnd = sequenceEnd;

    this.replayTopic = replayTopic;
  }

  public ReplayRequestEvent(int templateId, int entityId,
      String replayRequestIdentifier, String replayTopic, long sequenceStart,
      long sequenceEnd, Profile[] constraints) {
    this.templateId = templateId;
    this.entityId = entityId;

    this.replayRequestIdentifier = replayRequestIdentifier;
    if (replayRequestIdentifier != null) {
      hasRequestIdentifier = true;
    }
    this.replayTopic = replayTopic;
    if (replayTopic != null) {
      hasReplayTopic = true;
    }
    hasSpecifiedSequenceRange = true;
    this.sequenceStart = sequenceStart;
    this.sequenceEnd = sequenceEnd;

    this.constraints = constraints;
    if (constraints != null) {
      hasSpecifiedConstraints = true;
    }
  }

  public int getTemplateId() {
    return this.templateId;
  }

  public int getEntityId() {
    return this.entityId;
  }

  public boolean hasRequestIdentifier() {
    return this.hasRequestIdentifier;
  }

  public String getReplayIdentifier() {
    return this.replayRequestIdentifier;
  }

  public boolean hasReplayTopic() {
    return this.hasReplayTopic;
  }

  public String getReplayTopic() {
    return this.replayTopic;
  }

  public boolean hasListOfSequencesToReplay() {
    return this.hasListOfSequencesToReplay;
  }

  public long[] getListOfSequencesToReplay() {
    return this.listOfSequencesToReplay;
  }

  public boolean hasSpecifiedSequenceRange() {
    return this.hasSpecifiedSequenceRange;
  }

  public long getStartingSequenceNumber() {
    return this.sequenceStart;
  }

  public long getEndingSequenceNumber() {
    return this.sequenceEnd;
  }

  public boolean hasSpecifiedConstraints() {
    return this.hasSpecifiedConstraints;
  }

  public Profile[] getListOfSpecifiedConstraints() {
    return this.constraints;
  }

  public boolean hasReplayWithTimestampRange() {
    return this.replayWithTimestampRange;
  }

  public boolean hasTimestampRange() {
    return this.hasTimestampRange;
  }

  public long getStartTimestamp() {
    return this.startTimestamp;
  }

  public long getEndTimestamp() {
    return this.endTimestamp;
  }

  public boolean hasStartingSequenceAndEndingTimestamp() {
    return this.hasStartingSequenceAndEndingTimestamp;
  }

  public long getStartingSequence() {
    return this.startingSequence;
  }

  public long getEndingTimestamp() {
    return this.endingTimestamp;
  }

  public boolean hasRequestForTimestamp() {
    return requestForTimestamp;
  }

  public long getSequenceNumber() {
    return this.sequenceNumber;
  }

  public boolean hasRequestSequencesAndTimestamps() {
    return this.requestSequencesAndTimestamps;
  }

  public String getModuleName() {
    return this.moduleName;
  }

  public String toString() {
    String stringRep = moduleName + "\nTemplateId=" + templateId;
    stringRep += "\nEntityId=" + entityId;
    stringRep += "\nhasRequestIdentifier=" + hasRequestIdentifier;
    stringRep += "\nReplayRequestIdentifier=" + replayRequestIdentifier;
    stringRep += "\nhasReplayTopic=" + hasReplayTopic;
    stringRep += "\nReplayTopic=" + replayTopic;
    stringRep += "\nhasListOfSequencesToReplay=" + hasListOfSequencesToReplay;

    if (hasListOfSequencesToReplay) {
      String temp = "\nlistOfSequencesToReplay=";
      for (int i = 0; i < listOfSequencesToReplay.length; i++) {
        temp += "seq[" + i + "]: " + listOfSequencesToReplay[i] + " ";
      }
      stringRep += temp;
    } else {
      stringRep += "\nlistOfSequencesToReplay=NULL";
    }

    stringRep += "\nhasSpecifiedSequenceRange=" + hasSpecifiedSequenceRange;
    stringRep += "\nsequenceStart=" + sequenceStart;
    stringRep += "\nsequenceEnd=" + sequenceEnd;
    stringRep += "\nhasSpecifiedConstraints=" + hasSpecifiedConstraints;

    if (hasSpecifiedConstraints) {
      for (int i = 0; i < constraints.length; i++) {
        stringRep += ("\nconstraints[" + i + "]: " + constraints[i]);
      }
    }

    stringRep += "\nreplayWithTimestampRange=" + replayWithTimestampRange;
    stringRep += "\nhasTimestampRange=" + hasTimestampRange;
    stringRep += "\nstartTimestamp=" + startTimestamp;
    stringRep += "\nendTimestamp=" + endTimestamp;
    stringRep += "\nhasStartingSequenceAndEndingTimestamp="
        + hasStartingSequenceAndEndingTimestamp;
    stringRep += "\nstartingSequence=" + startingSequence;
    stringRep += "\nendingTimestamp=" + endingTimestamp;
    stringRep += "\nrequestForTimestamp=" + requestForTimestamp;
    stringRep += "\nsequenceNumber=" + sequenceNumber;
    stringRep += "\nrequestSequencesAndTimestamps="
        + requestSequencesAndTimestamps;
    return stringRep;
  }

  public static void main(String[] args) {
    ReplayRequestEvent req = new ReplayRequestEvent(23, 34, "testevent",
        "/home/nb/replaytopic", true, 2345678, 8764563);
    System.out.println("req:\n" + req.toString());
    System.out.println("\n****************************\n");
    ReplayRequestEvent req2 = new ReplayRequestEvent(req.getBytes());
    System.out.println("req2:\n" + req2.toString());

  }
}
